(** environment.ml *)

type ('a, 'b) env = { 
    e_elems : ('a, 'b) Hashtbl.t ref;    (** Contents of the environment *)
    e_parent : (('a, 'b) env) option;   (** The parent of this environment *)
};;


let new_env = fun e -> {
    e_elems = ref (Hashtbl.create 100);
    e_parent = Some (e);
};;

let empty_env = fun () -> {
    e_elems = ref (Hashtbl.create 100);
    e_parent = None;
};;

let bind_env e k v = if Hashtbl.mem !(e.e_elems) k then Hashtbl.remove !(e.e_elems) k;
    Hashtbl.add !(e.e_elems) k v;;

let rec exists_env e k = 
    if Hashtbl.mem !(e.e_elems) k then true else 
        match e.e_parent with 
            None -> false            (** We reached the root and found no binding *)
          | Some e' -> exists_env e' k
          
let rec get_env e k = 
    try
        Hashtbl.find !(e.e_elems) k 
    with Not_found ->
    ( 
        match e.e_parent with 
            None -> raise Not_found            (** We reached the root and found no binding *)
          | Some e' -> get_env e' k
    )

let get_hashtbl e = e.e_elems;;